const cron = require('cron')
const lodash = require('lodash')
import Base from "@/entities/Base"
import { DeepCopy } from "@/utils/Utils"
import { SourceDao } from "@/dao/sourceDao"
import { ArticleDao } from "@/dao/articleDao"
import { TaskJobDao } from "@/dao/taskJobDao"
import { TaskQueue } from "./taskQueue.service"
import { Puppeteer } from "@/puppeteer/puppeteer"
import { pageFns } from "@/puppeteer/utils/pageHelper"
import { initialWindow } from "@/puppeteer/utils/browser"
import { CronType, CronStatus, ArticleStatus, UsageScope } from "@/entities/Constant"

const spiderCron = {
    Initial: () => {
        spiderCron.jobs = {
            1: spiderJob,
            2: aiJob,
            3: publishJob
        }
        spiderCron.scheduledDict = {}
        spiderCron.taskQueue = new TaskQueue()
        spiderCron.taskQueue.on("task", (task) => {
            let { func, args } = task
            func(...args).then((res) => {
                // 任务完成后通知队列
                console.log("onTask", res)
                spiderCron.CloseWindow().then(() => {
                    spiderCron.taskQueue.onTaskDone()
                }).catch((err) => {
                    console.log("onTask", err)
                    spiderCron.taskQueue.onTaskDone()
                })
            })
        });
        let whereDict = {
            enable: 1,
            status: [CronStatus.Waiting, CronStatus.Executing],
        }
        TaskJobDao.fetchAll(whereDict).then(jobs => {
            for (let job of jobs) {
                spiderCron.dispatch(job)
            }
        })
    },
    GetPage: async () => {
        if (spiderCron.page) {
            return spiderCron.page
        }
        let { window, page } = await initialWindow()
        spiderCron.page = page
        spiderCron.window = window
        return spiderCron.page
    },
    CloseWindow: async () => {
        if (spiderCron.jobList().length <= 0) {
            let window = spiderCron.window
            spiderCron.page = null
            spiderCron.window = null
            await pageFns.close(window, 10 * 1000)
        }
    },
    // args: {usageScope:xxx,data:xxx,raw:1/0},0表示data是job对象
    Dispatch: (args) => {
        if (args.raw != 1) {
            spiderCron.dispatch(args.data)
        } else {
            TaskJobDao.saveSource(args.data, args.usageScope).then(job => {
                spiderCron.scheduledDict[job.id] = job
                spiderCron.enqueueTask(job, args.data)
            })
        }
    },
    dispatch: (job) => {
        if (spiderCron.scheduledDict[job.id]) {
            return
        }
        spiderCron.scheduledDict[job.id] = job
        let deepJob = DeepCopy(job);
        (async function (job) {
            let jobJob = JSON.parse(job.job)
            if (jobJob.cronType == CronType.Instant) {
                spiderCron.execute(job)
            } else if (jobJob.cronType == CronType.Specific) {
                const nowTime = new Date();
                const triggerTime = new Date(jobJob.triggerTime);
                if (triggerTime > nowTime) {
                    let j = new cron.CronJob(
                        triggerTime, // cronTime
                        function () {
                            spiderCron.execute(job)
                        }, // onTick
                        null, // onComplete
                        true, // start
                        'Asia/Shanghai'// timeZone
                    );
                    console.log("triggerTime", j)
                } else if (triggerTime == nowTime) {
                    spiderCron.execute(job)
                } else {
                    console.log("job", "已过期")
                    job.status = CronStatus.Timeout
                    TaskJobDao.save(job)
                }

            } else {
                let j = new cron.CronJob(
                    jobJob.triggerCron, // cronTime
                    function () {
                        spiderCron.execute(job)
                    }, // onTick
                    null, // onComplete
                    true, // start
                    'Asia/Shanghai',// timeZone
                );
                console.log("triggerTime", j)
            }
        })(deepJob);
    },
    // 进入具体处理
    execute: (job) => {
        spiderCron.jobs[job.usageScope].execute(job)
    },
    // 直接进入队列
    enqueueTask: (job, source) => {
        spiderCron.taskQueue.enqueueTask((job, source) => new Promise((resolve) => {
            spiderCron.jobs[job.usageScope].enqueueTask(job, source).then(err => {
                resolve(err)
            })
        }), job, source)
    },
    //获取队列中的任务
    jobList: () => {
        return spiderCron.taskQueue.taskQueue
    }
}

const spiderJob = {
    execute: (job) => {
        let jobSource = JSON.parse(job.source)
        if (jobSource.specific.is) {
            for (let id of jobSource.specific.ids) {
                SourceDao.fetchOne(id).then(source => {
                    spiderCron.enqueueTask(job, source)
                })
            }
        } else {
            SourceDao.fetchAll(jobSource.whereDict).then(sources => {
                for (let source of sources) {
                    spiderCron.enqueueTask(job, source)
                }
            })
        }
    },
    enqueueTask: async (job, source) => {
        let page = await spiderCron.GetPage()
        let res = await Puppeteer.spider(source, page)
        if (res.err) {
            return res.err
        }
        job.status = CronStatus.Success
        source.status = ArticleStatus.Gather
        let params = [
            { table: SourceDao.table, obj: source },
            { table: TaskJobDao.table, obj: job }
        ]
        console.log("res.data", res.data)
        let aiJob = TaskJobDao.jobForm(res.data, UsageScope.Ai)
        if (!lodash.isEmpty(job.jobCode)) {
            aiJob.jobCode = job.jobCode
            params.push({ table: TaskJobDao.table, obj: aiJob })
        }
        let err = await Base.saveTx(...params)
        if (err) {
            return err
        }
        //是否有一键3连
        if (!lodash.isEmpty(job.jobCode)) {
            spiderCron.enqueueTask(aiJob, res.data)
        }
        return null
    }
}

const aiJob = {
    canAi: (article) => {
        if (article.status < ArticleStatus.Gather) {
            return false
        }
        if (article.status >= ArticleStatus.Aigced) {
            return true
        }
        return true
    },
    execute: (job) => {
        let jobSource = JSON.parse(job.source)
        if (jobSource.specific.is) {
            for (let id of jobSource.specific.ids) {
                ArticleDao.fetchOne({ id }).then(article => {
                    if (aiJob.canAi(article)) {
                        spiderCron.enqueueTask(job, article)
                    }
                })
            }
        } else {
            ArticleDao.fetchAll(jobSource.whereDict).then(articles => {
                for (let article of articles) {
                    if (aiJob.canAi(article)) {
                        spiderCron.enqueueTask(job, article)
                    }
                }
            })
        }
    },
    enqueueTask: async (job, article) => {
        let page = await spiderCron.GetPage()
        let res = await Puppeteer.startAi(article, page)
        if (res.err) {
            return res.err
        }
        job.status = CronStatus.Success
        article.status = ArticleStatus.Aigced
        let params = [
            { table: ArticleDao.table, obj: article },
            { table: TaskJobDao.table, obj: job }
        ]
        let pubJob = TaskJobDao.jobForm(article, UsageScope.Publisher)
        if (!lodash.isEmpty(job.jobCode)) {
            pubJob.jobCode = job.jobCode
            params.push({ table: TaskJobDao.table, obj: pubJob })
        }
        let err = await Base.saveTx(...params)
        if (err) {
            return err
        }
        //是否有一键3连
        if (!lodash.isEmpty(job.jobCode)) {
            spiderCron.enqueueTask(pubJob, article)
        }
        return null
    }
}

const publishJob = {
    execute: (job) => {
        let jobSource = JSON.parse(job.source)
        if (jobSource.specific.is) {
            for (let id of jobSource.specific.ids) {
                ArticleDao.fetchOne({ id }).then(article => {
                    spiderCron.enqueueTask(job, article)
                })
            }
        } else {
            ArticleDao.fetchAll(jobSource.whereDict).then(articles => {
                for (let article of articles) {
                    spiderCron.enqueueTask(job, article)
                }
            })
        }
    },
    enqueueTask: async (job, article) => {
        let page = await spiderCron.GetPage()
        let res = await Puppeteer.startPost(article, page)
        if (res.err) {
            return res.err
        }
        job.status = CronStatus.Success
        article.status = ArticleStatus.Published
        let params = [
            { table: ArticleDao.table, obj: article },
            { table: TaskJobDao.table, obj: job }
        ]
        return await Base.saveTx(...params)
    }
}

export { spiderCron }